package com.yunyao.framework.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yunyao.common.constant.enums.AuditStatusEnums;
import com.yunyao.common.constant.enums.BindContractStatusEnums;
import com.yunyao.common.constant.enums.ContractPriceType;
import com.yunyao.common.constant.enums.DataDeletedStatus;
import com.yunyao.common.constant.enums.SaleContractType;
import com.yunyao.common.core.BaseContext;
import com.yunyao.common.core.UserInfoRequest;
import com.yunyao.common.dto.PurchaseDto;
import com.yunyao.common.dto.excel.ContractExcelDto;
import com.yunyao.common.dto.excel.ContractMaterialExcelDto;
import com.yunyao.common.exception.BusinessException;
import com.yunyao.common.web.enums.ResultMsgEnum;
import com.yunyao.common.web.util.ConvertUtil;
import com.yunyao.common.web.util.PageResult;
import com.yunyao.common.web.util.YResult;
import com.yunyao.dao.model.*;
import com.yunyao.dao.service.bamu.*;
import com.yunyao.framework.audit.PurchaseContractAuditHandle;
import com.yunyao.framework.dto.contract.ContractMaterialImportDto;
import com.yunyao.framework.dto.purchase.PurchaseContractApprovalDto;
import com.yunyao.framework.dto.purchase.PurchaseContractSaveDto;
import com.yunyao.framework.dto.purchase.PurchaseContractSubmitApprovalDto;
import com.yunyao.framework.excel.imports.PurchaseContractMaterialExcelImportHandle;
import com.yunyao.framework.service.PurchaseService;
import com.yunyao.framework.vo.purchase.PurchaseInfoVO;
import com.yunyao.framework.vo.purchase.PurchaseVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author xingjishuai
 * @date 2024-07-22
 */
@Slf4j
@Service
public class PurchaseServiceImpl implements PurchaseService {
    @Autowired
    private PurchaseContractAuditHandle purchaseContractAuditHandle;
    @Autowired
    private IBamuPurchaseContractService iBamuPurchaseContractService;
    @Autowired
    private IBamuContractWirePayService iBamuContractWirePayService;
    @Autowired
    private IBamuContractMaterialService iBamuContractMaterialService;
    @Autowired
    private IBamuContractNodePayService iBamuContractNodePayService;
    @Autowired
    private IBamuSaleContractService ibamuSaleContractService;
    @Autowired
    private PurchaseContractMaterialExcelImportHandle purchaseContractMaterialExcelImportHandle;
    @Autowired
    private IBamuSpReconciliationSettlementService iBamuSpReconciliationSettlementService;
    @Autowired
    private IBamuSpReconciliationSettlementOrderMaterialService iBamuSpReconciliationSettlementOrderMaterialService;

    @Override
    public YResult<PageResult<PurchaseVO>> pageList(PurchaseDto purchaseDto) {
        // 构建查询条件
        LambdaQueryWrapper<BamuPurchaseContract> queryWrapper = new LambdaQueryWrapper<BamuPurchaseContract>()
                .eq(BamuPurchaseContract::getContractType, purchaseDto.getContractType())
                .eq(BamuPurchaseContract::getIsDeleted, DataDeletedStatus.NO.getType())
                .in(CollectionUtils.isNotEmpty(BaseContext.getProjectList()), BamuPurchaseContract::getProjectId, BaseContext.getProjectList())
                .in(CollectionUtils.isNotEmpty(BaseContext.getSupplierList()), BamuPurchaseContract::getSupplierDeptId, BaseContext.getSupplierList())
                .between(StringUtils.isNotBlank(purchaseDto.getBeginDate()) && StringUtils.isNotBlank(purchaseDto.getEndDate()), BamuPurchaseContract::getSignDate,
                        ConvertUtil.getStartOfDay(ConvertUtil.getString2Date(purchaseDto.getBeginDate())), ConvertUtil.getEndOfDay(ConvertUtil.getString2Date(purchaseDto.getEndDate())))
                .like(StringUtils.isNotBlank(purchaseDto.getContractNo()), BamuPurchaseContract::getContractNo, purchaseDto.getContractNo())
                .like(StringUtils.isNotBlank(purchaseDto.getContractName()), BamuPurchaseContract::getContractName, purchaseDto.getContractName())
                .eq(StringUtils.isNotBlank(purchaseDto.getProjectId()), BamuPurchaseContract::getProjectId, purchaseDto.getProjectId())
                .like(StringUtils.isNotBlank(purchaseDto.getProjectName()), BamuPurchaseContract::getProjectName, purchaseDto.getProjectName())
                .eq(StringUtils.isNotBlank(purchaseDto.getSupplierDeptId()), BamuPurchaseContract::getSupplierDeptId, purchaseDto.getSupplierDeptId())
                .like(StringUtils.isNotBlank(purchaseDto.getSupplierDeptName()), BamuPurchaseContract::getSupplierDeptName, purchaseDto.getSupplierDeptName())
                .like(StringUtils.isNotBlank(purchaseDto.getMainContractNo()), BamuPurchaseContract::getMainContractNo, purchaseDto.getMainContractNo())
                .like(StringUtils.isNotBlank(purchaseDto.getMainContractName()), BamuPurchaseContract::getMainContractName, purchaseDto.getMainContractName())
                .like(StringUtils.isNotBlank(purchaseDto.getProjectDeptName()),
                        BamuPurchaseContract::getProjectDeptName, purchaseDto.getProjectDeptName())
                .eq(StringUtils.isNotBlank(purchaseDto.getContractMethod()), BamuPurchaseContract::getContractMethod
                        , purchaseDto.getContractMethod())
                .orderByDesc(BamuPurchaseContract::getCreatedAt)
                .orderByDesc(BamuPurchaseContract::getId);

        // 分页查询
        IPage<BamuPurchaseContract> contractPage = new Page<>(purchaseDto.getPageIndex(), purchaseDto.getPageSize());
        contractPage = iBamuPurchaseContractService.page(contractPage, queryWrapper);
        List<BamuPurchaseContract> records = contractPage.getRecords();
        // 处理查询结果
        if (CollectionUtils.isEmpty(records)) {
            return YResult.success(new PageResult<>());
        }

        // 转换为VO对象列表
        List<PurchaseVO> purchaseVOList = daoToVoList(records);

        // 返回分页结果
        return YResult.success(new PageResult<>(purchaseVOList, contractPage.getCurrent(), contractPage.getSize(), contractPage.getPages(), contractPage.getTotal()));
    }

    /**
     * 根据ID获取采购合同信息
     * <p>
     * 此方法通过调用数据访问层服务，根据合同ID查询采购合同的详细信息它首先构建一个查询包装器来确保
     * 只查询未被删除的特定ID的合同如果找到对应的合同信息，则将其转换为指定的值对象如果未找到，
     * 则返回一个表示没有数据的错误结果
     *
     * @param id 合同ID，用于标识需要查询的采购合同
     * @return 包含采购合同信息的YResult对象，或者表示错误的结果
     */
    @Override
    public YResult<PurchaseInfoVO> getPurchaseContractById(String id) {
        // 构建查询包装器，设置查询条件为未删除且ID匹配的合同
        LambdaQueryWrapper<BamuPurchaseContract> wrapper = new LambdaQueryWrapper<BamuPurchaseContract>().eq(BamuPurchaseContract::getId, id)
                .eq(BamuPurchaseContract::getIsDeleted, DataDeletedStatus.NO.getType());

        // 调用服务方法查询合同信息
        BamuPurchaseContract bamuPurchaseContract = iBamuPurchaseContractService.getOne(wrapper);

        // 处理查询结果
        if (Objects.isNull(bamuPurchaseContract)) {
            // 如果未找到合同信息，返回表示没有数据的错误结果
            return YResult.error(ResultMsgEnum.NO_DATA);
        }

        // 如果找到合同信息，将其转换为指定的值对象，并返回成功结果
        return YResult.success(daoToInfoVO(bamuPurchaseContract));
    }

    /**
     * 保存采购订单信息
     *
     * @param saveDto 采购订单保存DTO
     * @return 保存结果，包括采购订单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public YResult<String> saveInfo(PurchaseContractSaveDto saveDto) {
        try {
            // 校验输入参数
            if (saveDto == null) {
                return YResult.error(ResultMsgEnum.PARAM_ERROR);
            }
            // 创建采购订单实体
            BamuPurchaseContract purchaseContract = new BamuPurchaseContract();
            // 将DTO中的信息转换为实体类信息
            purchaseDtoToDaoInfo(purchaseContract, saveDto);
            // 获取销售订单ID
            String saleContractId = saveDto.getSaleContractId();
            // 判断是新增还是编辑
            boolean isNew = StringUtils.isBlank(saveDto.getId());
            if (isNew) {
                addPurchaseData(saveDto, purchaseContract, saleContractId);
            } else {
                updatePurchaseData(saveDto, purchaseContract, saleContractId);
            }
            // 返回保存结果
            return YResult.success(purchaseContract.getId());
        } catch (BusinessException businessException) {
            log.warn("保存采购订单异常", businessException);
            return YResult.error(businessException.getMessage());
        } catch (Exception exception) {
            // 异常处理
            log.error("保存采购订单异常", exception);
            return YResult.error(ResultMsgEnum.SYS_ERROR);
        }
    }

    /**
     * 添加采购数据
     * 本方法主要负责将销售合同数据转换并添加到采购合同中，确保采购数据的准确性和完整性
     *
     * @param saveDto          销售合同保存DTO，包含需要添加到采购合同中的详细信息
     * @param purchaseContract 采购合同对象，用于存储采购相关信息
     * @param saleContractId   销售合同ID，用于标识和关联销售合同
     */
    @Transactional(rollbackFor = Exception.class)
    public void addPurchaseData(PurchaseContractSaveDto saveDto, BamuPurchaseContract purchaseContract
            , String saleContractId) {
        // 新增时进行数据校验，确保传入的数据符合预期格式和规则
        newCheckData(saveDto);
        // 更新采购订单中的销售订单信息，将销售合同信息转换并更新到采购合同中
        saleInfoToPurchaseInfo(purchaseContract, saleContractId);
        // 保存采购订单，将采购合同信息存入数据库
        iBamuPurchaseContractService.save(purchaseContract);
        // 保存其他相关信息，可能包括采购订单的附件、备注等额外信息
        saveOtherInfo(purchaseContract, saleContractId);
        // 更新主信息，可能是指更新采购订单的状态或关联的销售订单信息等
        updateMainInfo(purchaseContract.getId());
        ibamuSaleContractService.updateBindById(saleContractId);
    }

    /**
     * 更新采购数据
     * 本方法主要用于编辑采购订单时，根据不同的情况更新采购订单的信息
     * 如果销售订单ID发生变化，则会更新采购订单的相关信息和其它相关数据
     * 如果是采购订单的差价发生变化，并且没有更改销售订单，则更新采购订单的总价
     * 最后，更新采购订单本身的信息以及主信息（如合同名称或编号变更时）
     *
     * @param saveDto          保存采购合同的DTO对象，包含采购合同的相关信息以及需要更新的数据
     * @param purchaseContract 采购合同对象，用于存储和更新采购合同的信息
     * @param saleContractId   销售订单ID，用于判断是否需要根据新的销售订单ID更新采购合同的信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchaseData(PurchaseContractSaveDto saveDto,
                                   BamuPurchaseContract purchaseContract, String saleContractId) {
        // 编辑时进行数据校验并获取已存在的采购订单
        BamuPurchaseContract existingContract = updateCheckData(saveDto);
        // 设置采购订单ID
        purchaseContract.setId(existingContract.getId());
        // 获取原有销售订单ID
        String contractSaleContractId = existingContract.getSaleContractId();
        // 判断是否需要更新采购合同总价
        boolean isUpdatePurchaseContractTotalPrice = true;
        if (StringUtils.isNotBlank(saleContractId) && StringUtils.isNotBlank(contractSaleContractId)
                && !StringUtils.equals(contractSaleContractId, saleContractId)) {
            // 销售订单变更时，更新采购订单信息
            saleInfoToPurchaseInfo(purchaseContract, saleContractId);

            //保存其他相关信息
            saveOtherUpdateInfo(purchaseContract, saleContractId);
            ibamuSaleContractService.updateUnBindById(contractSaleContractId);
            ibamuSaleContractService.updateBindById(saleContractId);
            // 标记无需更新采购合同总价
            isUpdatePurchaseContractTotalPrice = false;
        }
        // 获取差价
        BigDecimal diffPricePoints = ConvertUtil.getBigDecimalValue(saveDto.getDiffPricePoints(),BigDecimal.valueOf(0));
        BigDecimal contractDiffPricePoints = ConvertUtil.getBigDecimalValue(existingContract.getDiffPricePoints(),BigDecimal.valueOf(0));
        /**
         * 比较diffPricePoints 和 contractDiffPricePoints，如果不相等，则更新采购合同总价
         */
        if (diffPricePoints.compareTo(contractDiffPricePoints) != 0 && isUpdatePurchaseContractTotalPrice) {
            // 更新采购合同总价
            updatePurchaseContractTotalPrice(purchaseContract);
        }
        // 更新采购订单
        iBamuPurchaseContractService.updateById(purchaseContract);
        // 更新主信息（如合同名称或合同编号变更时）
        if (StringUtils.isNotBlank(saveDto.getContractName()) || StringUtils.isNotBlank(saveDto.getContractNo())) {
            updateMainInfo(purchaseContract.getId());
        }

    }


    /**
     * 更新采购合同总价
     * 本方法根据采购合同和销售合同的差异价点，计算采购合同的总价
     * 它首先获取采购合同和销售合同的所有材料，并根据差异价点调整每个材料的价格
     * 最后，将所有材料的含税总价相加，得到采购合同的总价
     *
     * @param purchaseContract 采购合同对象，包含采购合同和销售合同的ID及差异价点
     */
    private void updatePurchaseContractTotalPrice(BamuPurchaseContract purchaseContract) {
        // 获取采购合同ID和销售合同ID
        String purchaseContractId = purchaseContract.getId();
        String saleContractId = purchaseContract.getSaleContractId();
        // 获取差异价点
        BigDecimal diffPricePoints = purchaseContract.getDiffPricePoints();

        // 空值检查
        if (diffPricePoints == null) {
            // 如果差异价点为空，则不进行计算，直接返回
            return;
        }

        // 获取采购合同的所有材料
        List<BamuContractMaterial> purchaseContractMaterialList =
                iBamuContractMaterialService.listByContractId(purchaseContractId);
        // 如果采购合同材料为空，则不进行计算，直接返回
        if (CollectionUtils.isEmpty(purchaseContractMaterialList)) {
            return;
        }

        // 获取销售合同的所有材料
        List<BamuContractMaterial> saleContractMaterialList =
                iBamuContractMaterialService.listByContractId(saleContractId);
        // 将销售合同材料列表转换为Map，以便快速查找
        Map<String, BamuContractMaterial> saleContractMaterialMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(saleContractMaterialList)) {
            saleContractMaterialMap =
                    saleContractMaterialList.stream().collect(Collectors.toMap(BamuContractMaterial::getId, Function.identity(), (v1, v2) -> v1));
        }

        // 初始化合同总价
        BigDecimal contractTotalPrice = BigDecimal.ZERO;
        // 遍历采购合同的每个材料，计算调整后的价格并累加到合同总价中
        for (BamuContractMaterial bamuContractMaterial : purchaseContractMaterialList) {
            //bamuContractMaterial status为1的时候，表示材料已导入，不需要重新计算
//            if (1==ConvertUtil.getIntValue(bamuContractMaterial.getStatus(),0)){
//                return;
//            }
            // 获取材料ID
            String contractMaterialId = bamuContractMaterial.getContractMaterialId();
            // 计算价格调整比例
            BigDecimal percentage =
                    BigDecimal.ONE.subtract(diffPricePoints.divide(new BigDecimal("100"), 2,
                            RoundingMode.HALF_UP));

            // 检查材料ID是否不为空
            if (StringUtils.isNotBlank(contractMaterialId)) {
                // 检查该材料是否在销售合同材料中
                boolean isSaleMaterial = saleContractMaterialMap.containsKey(contractMaterialId);
                if (isSaleMaterial) {
                    // 获取对应材料的销售合同信息
                    BamuContractMaterial saleContractMaterial =
                            saleContractMaterialMap.get(contractMaterialId);
                    // 获取销售合同材料的含税总价
                    BigDecimal includingTaxTotalPrice =
                            saleContractMaterial.getIncludingTaxTotalPrice();

                    // 检查含税总价是否不为空
                    if (includingTaxTotalPrice != null) {
                        // 根据调整比例计算调整后的含税总价
                        BigDecimal includingTaxTotalPriceAdjusted =
                                includingTaxTotalPrice.multiply(percentage);
                        bamuContractMaterial.setIncludingTaxTotalPrice(includingTaxTotalPriceAdjusted);
                        // 获取材料数量
                        BigDecimal materialNum = bamuContractMaterial.getMaterialNum();
                        // 检查材料数量是否大于0，避免除以0
                        if (materialNum.longValue() > BigDecimal.ZERO.longValue()) {
                            // 计算调整后的含税单价
                            BigDecimal includingTaxUnitPrice = ConvertUtil.convertTaxTotal2UnitPrice(includingTaxTotalPriceAdjusted, materialNum, bamuContractMaterial.getTax()).getIncludingTaxUnitPrice();
//                                    includingTaxTotalPriceAdjusted.divide(materialNum, 4,
//                                            RoundingMode.HALF_UP);
                            bamuContractMaterial.setIncludingTaxUnitPrice(includingTaxUnitPrice);

                            // 计算不含税单价
//                            BigDecimal tax =
//                                    BigDecimal.ONE.subtract(bamuContractMaterial.getTax().divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));
                            BigDecimal excludingTaxUnitPrice = ConvertUtil.convertTaxTotal2UnitPrice(includingTaxTotalPriceAdjusted, materialNum, bamuContractMaterial.getTax()).getExcludingTaxUnitPrice();
                            bamuContractMaterial.setExcludingTaxUnitPrice(excludingTaxUnitPrice);
                        }
                    }
                }
            }
            // 累加材料的含税总价到合同总价
            contractTotalPrice =
                    contractTotalPrice.add(bamuContractMaterial.getIncludingTaxTotalPrice());
        }
        // 批量更新采购合同材料
        iBamuContractMaterialService.updateBatchById(purchaseContractMaterialList, 50);
        // 更新采购合同的总价
        purchaseContract.setContractTotalPrice(contractTotalPrice);
        // 更新采购合同
        iBamuPurchaseContractService.updateById(purchaseContract);
    }


    /**
     * 根据采购合同ID更新主合同信息
     * 该方法主要功能是根据传入的采购合同ID，更新该采购合同的主合同信息
     * 如果传入的采购合同不存在，或者不是销售合同或主合同，则不进行更新操作
     * 如果是销售合同，则根据销售合同ID查找对应的主合同，并更新采购合同的主合同信息
     * 如果是主合同，则直接将当前采购合同设为主合同，并更新相关信息
     *
     * @param purchaseContractId 采购合同ID
     */
    private void updateMainInfo(String purchaseContractId) {
        // 根据采购合同ID获取采购合同信息，不包括已删除的记录
        BamuPurchaseContract bamuPurchaseContract =
                iBamuPurchaseContractService.getByIdNoDelete(purchaseContractId);
        // 如果采购合同不存在，则直接返回
        if (Objects.isNull(bamuPurchaseContract)) {
            return;
        }
        // 获取采购合同类型
        String contractType = bamuPurchaseContract.getContractType();
        // 如果不是销售合同或主合同类型，则直接返回
        if (!StringUtils.equals(String.valueOf(SaleContractType.SALE.getType()),
                contractType) && !StringUtils.equals(String.valueOf(SaleContractType.MASTER.getType()),
                contractType)) {
            return;
        }
        // 如果是销售合同
        if (StringUtils.equals(String.valueOf(SaleContractType.SALE.getType()),
                contractType)) {
            // 获取销售合同ID
            String saleContractId = bamuPurchaseContract.getSaleContractId();
            // 如果销售合同ID为空或空白，则直接返回
            if (StringUtils.isBlank(saleContractId)) {
                return;
            }
            // 根据销售合同ID获取销售合同信息，不包括已删除的记录
            BamuSaleContract bamuSaleContract =
                    ibamuSaleContractService.getByIdNoDelete(saleContractId);
            // 如果销售合同不存在，则直接返回
            if (Objects.isNull(bamuSaleContract)) {
                return;
            }
            // 获取主合同ID
            String mainContractId = bamuSaleContract.getMainContractId();
            // 如果主合同ID为空或空白，则直接返回
            if (StringUtils.isBlank(mainContractId)) {
                return;
            }
            // 根据销售合同的主合同ID获取主采购合同信息
            BamuPurchaseContract mainPurchaseContract =
                    iBamuPurchaseContractService.getBySaleContractId(mainContractId);
            // 如果主采购合同不存在，则直接返回
            if (Objects.isNull(mainPurchaseContract)) {
                return;
            }
            // 更新当前采购合同的主合同ID、主合同编号和主合同名称
            bamuPurchaseContract.setMainContractId(mainPurchaseContract.getId());
            bamuPurchaseContract.setMainContractNo(mainPurchaseContract.getContractNo());
            bamuPurchaseContract.setMainContractName(mainPurchaseContract.getContractName());
        } else {
            // 如果是主合同，则直接将当前采购合同设为主合同，并更新相关信息
            bamuPurchaseContract.setMainContractId(bamuPurchaseContract.getId());
            bamuPurchaseContract.setMainContractNo(bamuPurchaseContract.getContractNo());
            bamuPurchaseContract.setMainContractName(bamuPurchaseContract.getContractName());
        }
        // 更新采购合同信息
        iBamuPurchaseContractService.updateById(bamuPurchaseContract);
    }


    /**
     * 根据销售合同ID查询销售合同信息，并将信息复制到采购合同中
     * 此方法用于从销售合同派生出采购合同，复制重要信息以简化采购合同的创建过程
     *
     * @param purchaseContract 采购合同对象，用于存储生成的采购合同信息
     * @param saleContractId   销售合同ID，用于查询对应的销售合同信息
     */
    private void saleInfoToPurchaseInfo(BamuPurchaseContract purchaseContract,
                                        String saleContractId) {
        // 根据销售合同ID获取销售合同信息
        BamuSaleContract bamuSaleContract =
                ibamuSaleContractService.getByIdNoDelete(saleContractId);
        if (Objects.isNull(bamuSaleContract)) {
            log.error("根据销售合同ID获取销售合同信息失败");
            return;
        }
        // 以下代码将销售合同的相关信息复制到采购合同中
        purchaseContract.setSaleContractId(bamuSaleContract.getId());
        purchaseContract.setSaleContractNo(bamuSaleContract.getContractNo());
        purchaseContract.setSaleContractName(bamuSaleContract.getContractName());
        purchaseContract.setPurchaseDeptId(bamuSaleContract.getSupplierDeptId());
        purchaseContract.setPurchaseDeptName(bamuSaleContract.getSupplierDeptName());
        purchaseContract.setSupplierDeptId(bamuSaleContract.getSupplierDeptId());
        purchaseContract.setSupplierDeptName(bamuSaleContract.getSupplierDeptName());
        purchaseContract.setSupplierCategoryId(bamuSaleContract.getSupplierCategoryId());
        purchaseContract.setSupplierCategoryName(bamuSaleContract.getSupplierCategoryName());
        purchaseContract.setPurchasePersonId(bamuSaleContract.getPurchasePersonId());
        purchaseContract.setPurchasePersonName(bamuSaleContract.getPurchasePersonName());
        purchaseContract.setPurchasePhone(bamuSaleContract.getPurchasePhone());
        purchaseContract.setProjectId(bamuSaleContract.getProjectId());
        purchaseContract.setProjectName(bamuSaleContract.getProjectName());
        purchaseContract.setProjectDeptId(bamuSaleContract.getProjectDeptId());
        purchaseContract.setProjectDeptName(bamuSaleContract.getProjectDeptName());
        purchaseContract.setProjectManager(bamuSaleContract.getProjectManager());
        purchaseContract.setInquiryNo(bamuSaleContract.getInquiryNo());
        purchaseContract.setProjectAddress(bamuSaleContract.getProjectAddress());
        purchaseContract.setProjectManagerId(bamuSaleContract.getProjectManagerId());
        purchaseContract.setContractNum(bamuSaleContract.getContractNum());
        BigDecimal diffPricePoints = purchaseContract.getDiffPricePoints();
        // 计算99%的百分比
        BigDecimal percentage =
                BigDecimal.ONE.subtract(diffPricePoints.divide(new BigDecimal("100"), 2,
                        RoundingMode.HALF_UP));

        // 使用99%的百分比来更新合同总价
        BigDecimal contractTotalPriceAdjusted =
                percentage.multiply(bamuSaleContract.getContractTotalPrice());
        purchaseContract.setContractTotalPrice(contractTotalPriceAdjusted);
        purchaseContract.setContractMethod(bamuSaleContract.getContractMethod());
        purchaseContract.setSupplyDeptId(bamuSaleContract.getSupplyDeptId());
        purchaseContract.setSupplyDeptName(bamuSaleContract.getSupplyDeptName());
    }

    /**
     * 将DTO对象转换为DAO对象的信息
     * 该方法负责将购买合同的保存DTO（PurchaseContractSaveDto）转换为购买合同实体（BamuPurchaseContract）
     * 用于存储过程中的数据填充，确保从界面接收到的信息能够准确地映射到持久层对象上
     *
     * @param purchaseContract 购买合同实体，将被填充数据的DAO对象
     * @param saveDto          包含购买合同信息的DTO，用于从界面接收数据
     */
    private void purchaseDtoToDaoInfo(BamuPurchaseContract purchaseContract,
                                      PurchaseContractSaveDto saveDto) {
        // 合同编号
        String contractNo = saveDto.getContractNo();
        if (StringUtils.isNotBlank(contractNo)) {
            purchaseContract.setContractNo(contractNo);
        }
        String saleContractId = saveDto.getSaleContractId();
        if (StringUtils.isNotBlank(saleContractId)) {
            purchaseContract.setSaleContractId(saleContractId);
        }
        // 合同名称
        String contractName = saveDto.getContractName();
        if (StringUtils.isNotBlank(contractName)) {
            purchaseContract.setContractName(contractName);
        }
        // 差价点数
        BigDecimal diffPricePoints = saveDto.getDiffPricePoints();
        if (Objects.nonNull(diffPricePoints)) {
            purchaseContract.setDiffPricePoints(diffPricePoints);
        }
        // 合同返点
        BigDecimal contractRebatePoints = saveDto.getContractRebatePoints();
        if (Objects.nonNull(contractRebatePoints)) {
            purchaseContract.setContractRebatePoints(contractRebatePoints);
        }
        // 合同总价
        BigDecimal contractTotalRealPrice = saveDto.getContractTotalRealPrice();
        if (Objects.nonNull(contractTotalRealPrice)) {
            purchaseContract.setContractTotalRealPrice(contractTotalRealPrice);
        }
        // 合同总价标记
        String contractTotalRealPriceMark = saveDto.getContractTotalRealPriceMark();
        if (StringUtils.isNotBlank(contractTotalRealPriceMark)) {
            purchaseContract.setContractTotalRealPriceMark(contractTotalRealPriceMark);
        }
        // 签约日期
        String signDate = saveDto.getSignDate();
        if (StringUtils.isNotBlank(signDate)) {
            purchaseContract.setSignDate(ConvertUtil.getString2Date(signDate));
        }
        // 付款截止日期
        String paymentDeadlineDate = saveDto.getPaymentDeadlineDate();
        if (StringUtils.isNotBlank(paymentDeadlineDate)) {
            purchaseContract.setPaymentDeadlineDate(ConvertUtil.getString2Date(paymentDeadlineDate));
        }
        // 备注
        String mark = saveDto.getMark();
        if (StringUtils.isNotBlank(mark)) {
            purchaseContract.setMark(mark);
        }
        // 合同文件
        String contractFile = saveDto.getContractFile();
        if (StringUtils.isNotBlank(contractFile)) {
            purchaseContract.setContractFile(contractFile);
        }
        // 合同类型
        String contractType = saveDto.getContractType();
        if (StringUtils.isNotBlank(contractType)) {
            purchaseContract.setContractType(contractType);
        }
    }


    /**
     * 更新采购合同前的数据检查方法
     * <p>
     * 此方法通过ID获取现有的采购合同，确保在更新之前，采购合同是存在的并且未被标记为删除
     * 如果未找到对应的采购合同，将抛出一个业务异常，提示未查询到相关数据
     *
     * @param saveDto 包含采购合同ID的保存DTO，用于定位特定的采购合同
     * @return 返回查找到的采购合同对象，以便后续的更新操作
     * @throws BusinessException 如果找不到指定ID的采购合同，抛出此异常
     */
    private BamuPurchaseContract updateCheckData(PurchaseContractSaveDto saveDto) {
        // 根据ID获取未删除的现有采购合同
        String id = saveDto.getId();
        BamuPurchaseContract existingContract = iBamuPurchaseContractService.getByIdNoDelete(id);
        // 如果未找到采购合同，则抛出异常
        if (existingContract == null) {
            throw new BusinessException("采购合同id: " + id + " 未查询到相关数据");
        }
        String saleContractId = saveDto.getSaleContractId();
        if (StringUtils.isNotBlank(saleContractId) && !StringUtils.equals(saleContractId,
                existingContract.getSaleContractId())) {
            checkSaleData(saleContractId);
        }
        // 返回找到的采购合同，以供后续更新操作使用
        return existingContract;
    }

    /**
     * 检查销售数据的有效性
     * 包括检查销售合同是否存在以及是否已绑定采购订单
     *
     * @param saleContractId 销售合同ID
     * @throws BusinessException 如果销售合同不存在或已绑定采购订单，则抛出业务异常
     */
    private void checkSaleData(String saleContractId) {
        // 获取销售合同ID，并查询销售合同数据，确保其存在
        BamuSaleContract bamuSaleContract =
                ibamuSaleContractService.getByIdNoDelete(saleContractId);
        if (Objects.isNull(bamuSaleContract)) {
            throw new BusinessException("销售合同id: " + saleContractId + " 未查询到相关数据");
        }
        // 检查销售合同是否已绑定采购订单
        String bindContractStatus = bamuSaleContract.getBindContractStatus();
        if (StringUtils.equals(bindContractStatus, BindContractStatusEnums.YES.getTypeStr())) {
            throw new BusinessException("销售合同id: " + saleContractId + " 已绑定采购订单");
        }
    }


    /**
     * 新增采购订单时进行数据校验
     *
     * @param saveDto 保存采购订单的数据传输对象，包含采购订单的相关信息
     *                <p>
     *                校验内容包括：
     *                1. 校验销售合同ID是否存在，以及是否已绑定采购订单
     *                2. 校验合同编号和合同名称是否为空
     *                3. 校验合同编号是否重复
     *                4. 校验供应商ID、签订日期、合同差价点和返利点是否为空或无效
     */
    private void newCheckData(PurchaseContractSaveDto saveDto) {
        // 确保销售合同id不可为空
        String saleContractId = saveDto.getSaleContractId();
        if (StringUtils.isBlank(saleContractId)) {
            throw new BusinessException("销售合同id不可为空");
        }
        // 获取销售合同ID，并查询销售合同数据，确保其存在
        checkSaleData(saleContractId);
        // 确保合同编号不为空
        String contractNo = saveDto.getContractNo();
        if (StringUtils.isBlank(contractNo)) {
            throw new BusinessException("合同编号不可为空");
        }
        // 确保合同名称不为空
        String contractName = saveDto.getContractName();
        if (StringUtils.isBlank(contractName)) {
            throw new BusinessException("合同名称不可为空");
        }
        // 检查合同编号是否重复
        BamuPurchaseContract purchaseContract =
                iBamuPurchaseContractService.getByContractNonDeleted(contractNo);
        if (Objects.nonNull(purchaseContract)) {
            throw new BusinessException("合同编号不可重复");
        }
        // 确保供应商ID不为空
        String supplyDeptId = saveDto.getSupplyDeptId();
        if (StringUtils.isBlank(supplyDeptId)) {
            throw new BusinessException("供应商ID");
        }
        // 确保签订日期不为空
        String signDate = saveDto.getSignDate();
        if (StringUtils.isBlank(signDate)) {
            throw new BusinessException("签订日期不可为空");
        }
        // 确保合同差价点不为空
        BigDecimal diffPricePoints = saveDto.getDiffPricePoints();
        if (Objects.isNull(diffPricePoints)) {
            throw new BusinessException("合同差价点(%)不可为空");
        }
        // 确保返利点不为空
        BigDecimal contractRebatePoints = saveDto.getContractRebatePoints();
        if (Objects.isNull(contractRebatePoints)) {
            throw new BusinessException("返利点（%）不可为空");
        }
    }


    private void saveOtherInfo(BamuPurchaseContract purchaseContract, String saleContractId) {
        String purchaseContractId = purchaseContract.getId();
        // 保存关联支付方式信息
        saveWirePay(purchaseContractId, saleContractId);
        // 保存节点支付信息
        saveNodePays(purchaseContractId, saleContractId);
        // 保存材料信息
        saveMaterial(purchaseContract, saleContractId);
    }

    private void saveOtherUpdateInfo(BamuPurchaseContract purchaseContract, String saleContractId) {
        String purchaseContractId = purchaseContract.getId();
        // 保存关联支付方式信息
        saveWirePay(purchaseContractId, saleContractId);
        // 保存节点支付信息
        saveNodePays(purchaseContractId, saleContractId);

        // 保存材料信息
        saveMaterial(purchaseContract, saleContractId);
    }


    /**
     * 保存关联支付方式信息
     *
     * @param purchaseContractId 采购合同ID
     * @param saleContractId     销售合同ID
     *                           <p>
     *                           该方法的目的是根据提供的采购和销售合同ID，更新关联的支付方式信息
     *                           首先，它尝试获取与采购合同关联的支付方式，并删除它
     *                           然后，它获取与销售合同关联的支付方式，并将其复制给采购合同
     *                           如果销售合同没有关联的支付方式，抛出运行时异常
     */
    private void saveWirePay(String purchaseContractId, String saleContractId) {
        // 根据采购合同ID获取关联的支付方式
        BamuContractWirePay purchaseContractWirePay =
                iBamuContractWirePayService.getByContractId(purchaseContractId);
        // 如果采购合同支付方式存在，则删除之
        if (Objects.nonNull(purchaseContractWirePay)) {
            iBamuContractWirePayService.deletedById(purchaseContractWirePay.getId());
        }
        // 根据销售合同ID获取关联的支付方式
        BamuContractWirePay saleContractWirePay =
                iBamuContractWirePayService.getByContractId(saleContractId);
        // 如果销售合同没有支付方式信息，抛出异常
        if (Objects.isNull(saleContractWirePay)) {
            throw new BusinessException("销售合同ID: " + saleContractId + "未查询到支付方式信息");
        }
        // 复制销售合同的支付方式给采购合同
        BamuContractWirePay contractWirePay = new BamuContractWirePay();
        BeanUtils.copyProperties(saleContractWirePay, contractWirePay);
        // 更新支付方式的合同ID为采购合同ID
        contractWirePay.setContractId(purchaseContractId);
        //并清除原有ID
        contractWirePay.setId(null);
        // 保存新的支付方式信息
        iBamuContractWirePayService.save(contractWirePay);
    }


    /**
     * 保存节点付款方式，将销售合同的付款方式复制到采购合同中
     *
     * @param purchaseContractId 采购合同ID
     * @param saleContractId     销售合同ID
     */
    private void saveNodePays(String purchaseContractId, String saleContractId) {
        // 查询采购合同的节点付款方式
        List<BamuContractNodePay> purchaseContractNodePays =
                iBamuContractNodePayService.listByContractId(purchaseContractId);
        // 如果采购合同的节点付款方式存在，则删除它们
        if (CollectionUtils.isNotEmpty(purchaseContractNodePays)) {
            iBamuContractNodePayService.deletedByContractId(purchaseContractId);
        }
        // 查询销售合同的节点付款方式
        List<BamuContractNodePay> saleContractNodePays =
                iBamuContractNodePayService.listByContractId(saleContractId);
        // 如果销售合同的节点付款方式不存在，则抛出异常
        if (CollectionUtils.isEmpty(saleContractNodePays)) {
            throw new BusinessException("销售合同ID: " + saleContractId + "未查询到合同付款方式节点");
        }
        // 将销售合同的节点付款方式复制到采购合同中，修改节点的合同ID并清除原有ID
        List<BamuContractNodePay> contractNodePayList =
                saleContractNodePays.stream().peek(nodePay -> {
                    nodePay.setContractId(purchaseContractId);
                    nodePay.setId(null);
                }).toList();
        try {
            // 批量保存新的节点付款方式到采购合同
            iBamuContractNodePayService.saveBatch(contractNodePayList);
        } catch (Exception e) {
            // 记录错误日志
            log.error("保存物料信息失败，原因：{}", e.getMessage(), e);
            throw new RuntimeException("保存物料信息失败", e);
        }
    }

    /**
     * 保存物料信息
     *
     * @param purchaseContract 采购合同对象，包含采购合同的相关信息
     * @param saleContractId   销售合同ID，用于获取销售合同相关的物料信息
     */
    private void saveMaterial(BamuPurchaseContract purchaseContract, String saleContractId) {
        // 获取采购合同ID
        String purchaseContractId = purchaseContract.getId();

        // 获取采购合同相关的物料列表
        List<BamuContractMaterial> purchaseContractMaterialList =
                iBamuContractMaterialService.listByContractId(purchaseContractId);

        // 如果存在现有物料，则删除它们
        if (CollectionUtils.isNotEmpty(purchaseContractMaterialList)) {
            log.warn("即将删除与采购合同ID: {} 相关的所有物料信息", purchaseContractId);
            iBamuContractMaterialService.deletedByContractId(purchaseContractId);
        }

        // 获取销售合同相关的物料列表
        List<BamuContractMaterial> saleContractMaterialList =
                iBamuContractMaterialService.listByContractId(saleContractId);

        // 检查销售合同是否有物料信息
        if (CollectionUtils.isEmpty(saleContractMaterialList)) {
            throw new BusinessException("销售合同ID: " + saleContractId + " 未查询到有效的材料信息");
        }

        // 获取采购合同的差价比例
        BigDecimal diffPricePoints = purchaseContract.getDiffPricePoints();

        // 初始化物料列表
        ArrayList<BamuContractMaterial> contractMaterialList = Lists.newArrayList();

        // 初始化合同总价
        BigDecimal contractTotalPrice = BigDecimal.ZERO;

        // 遍历物料列表，处理每个物料的信息
        for (BamuContractMaterial contractMaterial : saleContractMaterialList) {
            BamuContractMaterial bamuContractMaterial = new BamuContractMaterial();
            bamuContractMaterial.setContractId(purchaseContractId);

            // 将销售合同的物料信息转换为采购合同的物料信息
            saleToPurchaseMaterialInfo(contractMaterial, bamuContractMaterial);
            // 计算调整后的含税总价
            BigDecimal percentage = BigDecimal.ONE;
            if (diffPricePoints.longValue() > BigDecimal.ZERO.longValue()) {
                // 计算调整后的含税总价
                percentage =
                        BigDecimal.ONE.subtract(diffPricePoints.divide(new BigDecimal("100"), 2,
                                RoundingMode.HALF_UP));
            }
            BigDecimal includingTaxTotalPriceAdjusted =
                    contractMaterial.getIncludingTaxTotalPrice().multiply(percentage);
            bamuContractMaterial.setIncludingTaxTotalPrice(includingTaxTotalPriceAdjusted);

            // 累加合同总价
            contractTotalPrice = contractTotalPrice.add(includingTaxTotalPriceAdjusted);

            // 计算调整后的含税单价
            BigDecimal includingTaxUnitPrice =
                    includingTaxTotalPriceAdjusted.divide(contractMaterial.getMaterialNum(), 4,
                            RoundingMode.HALF_UP);
            bamuContractMaterial.setIncludingTaxUnitPrice(includingTaxUnitPrice);

            // 计算调整后的不含税单价
            BigDecimal tax =
                    BigDecimal.ONE.subtract(contractMaterial.getTax().divide(new BigDecimal("100"), 2,
                            RoundingMode.HALF_UP));
            BigDecimal excludingTaxUnitPrice = includingTaxUnitPrice.multiply(tax);
            bamuContractMaterial.setExcludingTaxUnitPrice(excludingTaxUnitPrice);

            // 设置租户ID
            bamuContractMaterial.setTenantId(contractMaterial.getTenantId());

            // 将处理后的物料信息添加到列表中
            contractMaterialList.add(bamuContractMaterial);
        }

        // 设置合同总价
        purchaseContract.setContractTotalPrice(contractTotalPrice);

        // 批量保存物料信息
        try {
            iBamuContractMaterialService.saveBatch(contractMaterialList, 50);
        } catch (Exception e) {
            // 记录错误日志
            log.error("保存物料信息失败，原因：{}", e.getMessage(), e);
            throw new RuntimeException("保存物料信息失败", e);
        }
    }

    /**
     * 将采购材料信息从销售合同复制到采购合同材料信息
     *
     * @param contractMaterial     销售合同中的材料信息
     * @param bamuContractMaterial 采购合同材料信息对象，将被填充来自contractMaterial的数据
     */
    private void saleToPurchaseMaterialInfo(BamuContractMaterial contractMaterial,
                                            BamuContractMaterial bamuContractMaterial) {
        // 复制材料ID
        bamuContractMaterial.setMaterialId(contractMaterial.getMaterialId());
        // 复制材料编码
        bamuContractMaterial.setMaterialCode(contractMaterial.getMaterialCode());
        // 复制材料材质
        bamuContractMaterial.setMaterialTexture(contractMaterial.getMaterialTexture());
        // 复制材料名称
        bamuContractMaterial.setMaterialName(contractMaterial.getMaterialName());
        // 复制材料规格
        bamuContractMaterial.setMaterialSpecs(contractMaterial.getMaterialSpecs());
        // 复制材料型号
        bamuContractMaterial.setMaterialModel(contractMaterial.getMaterialModel());
        // 复制材料单位
        bamuContractMaterial.setMaterialUnit(contractMaterial.getMaterialUnit());
        // 复制材料品牌
        bamuContractMaterial.setMaterialBrand(contractMaterial.getMaterialBrand());
        // 复制材料数量
        bamuContractMaterial.setMaterialNum(contractMaterial.getMaterialNum());
        // 复制材料税码
        bamuContractMaterial.setMaterialTaxCode(contractMaterial.getMaterialTaxCode());
        // 复制材料税名简称
        bamuContractMaterial.setMaterialTaxShortName(contractMaterial.getMaterialTaxShortName());
        // 复制材料标识
        bamuContractMaterial.setMaterialMark(contractMaterial.getMaterialMark());
        // 复制是否新增标识
        bamuContractMaterial.setIsAdd(contractMaterial.getIsAdd());
        // 复制名称是否相同标识
        bamuContractMaterial.setNameIsSame(contractMaterial.getNameIsSame());
        // 复制规格是否相同标识
        bamuContractMaterial.setSpecsIsSame(contractMaterial.getSpecsIsSame());
        // 复制型号是否相同标识
        bamuContractMaterial.setModelIsSame(contractMaterial.getModelIsSame());
        // 复制单位是否相同标识
        bamuContractMaterial.setUnitIsSame(contractMaterial.getUnitIsSame());
        // 复制材料排序号
        bamuContractMaterial.setMaterialSortNum(contractMaterial.getMaterialSortNum());
        // 复制合同材料ID
        bamuContractMaterial.setContractMaterialId(contractMaterial.getId());
        // 复制是否复制标识
        bamuContractMaterial.setIsCopy(contractMaterial.getIsCopy());
        // 复制申请数量
        bamuContractMaterial.setApplyNum(contractMaterial.getApplyNum());
        // 复制审核数量
        bamuContractMaterial.setCheckNum(contractMaterial.getCheckNum());
        // 复制销售合同不含税单价
        bamuContractMaterial.setSaleContractExcludingTaxUnitPrice(contractMaterial.getExcludingTaxUnitPrice());
        // 复制税率
        bamuContractMaterial.setTax(contractMaterial.getTax());
    }


    /**
     * 根据ID删除数据
     * <p>
     * 此方法用于处理删除操作。它首先尝试执行删除逻辑，
     * 如果出现异常，会记录异常信息并返回错误结果。
     *
     * @param id 要删除的数据的唯一标识
     * @return YResult<String> 删除操作的结果，包括成功或错误信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public YResult<String> deleted(String id) {
        try {
            // 尝试执行删除操作
            return deletedExecute(id);
        } catch (Exception e) {
            // 记录异常信息
            log.error("Error occurred while deleting with id: {}", id, e);
            // 捕获异常，返回系统错误信息
            return YResult.error(ResultMsgEnum.SYS_ERROR);
        }
    }


    /**
     * 执行删除采购合同操作
     * 此方法首先尝试获取指定ID的采购合同信息如果合同不存在，则返回错误结果；
     * 如果合同已经被标记为删除，则返回错误结果；
     * 否则，将合同标记为已删除，并更新相关销售合同的绑定状态，最后返回删除成功的结果
     *
     * @param id 采购合同的ID
     * @return 删除操作的结果，包含成功或错误信息
     */
    @Transactional(rollbackFor = Exception.class)
    public YResult<String> deletedExecute(String id) {
        // 根据ID获取采购合同信息
        BamuPurchaseContract bamuPurchaseContract = iBamuPurchaseContractService.getById(id);
        // 如果合同不存在，返回无数据错误
        if (Objects.isNull(bamuPurchaseContract)) {
            return YResult.error(ResultMsgEnum.NO_DATA);
        }
        // 获取合同的删除状态
        Integer isDeleted = bamuPurchaseContract.getIsDeleted();
        // 如果合同已经被删除，返回删除错误
        if (DataDeletedStatus.YES.getType() == isDeleted) {
            return YResult.error(ResultMsgEnum.DELETED_DELETED_DATA_ERROR);
        }
        // 获取销售合同ID，用于后续操作
        String saleContractId = bamuPurchaseContract.getSaleContractId();
        // 标记采购合同为已删除
        iBamuPurchaseContractService.deletedById(id);
        // 更新销售合同的绑定状态
        ibamuSaleContractService.updateUnBindById(saleContractId);
        // 返回删除成功的结果
        return YResult.success(id);
    }


    /**
     * 提交采购合同审批
     * <p>
     * 本方法用于提交采购合同的审批流程它尝试调用一个特定的方法来处理审批提交，
     * 如果成功提交，返回一个成功的结果，否则捕获异常并返回一个错误结果
     *
     * @param approvalDto 包含审批信息的对象，特别是审批的ID
     * @return YResult 包含审批结果的对象，成功时包含审批ID，失败时包含错误信息
     */
    @Override
    public YResult<String> submitApproval(PurchaseContractSubmitApprovalDto approvalDto) {
        try {
            // 调用审批处理方法来提交审批
            purchaseContractAuditHandle.submit(approvalDto.getId());
            // 如果审批提交成功，返回一个包含审批ID的成功结果
            return YResult.success(approvalDto.getId());
        } catch (Exception e) {
            // 如果审批提交过程中发生异常，记录错误日志
            log.error("提交审批异常", e);
            // 如果审批提交过程中发生异常，返回错误结果，包含异常信息
            return YResult.error(ResultMsgEnum.SUBMIT_APPROVAL_ERROR);
        }
    }


    /**
     * 执行采购合同审批操作
     *
     * @param approvalDto 采购合同审批数据传输对象，包含审批所需的各种信息
     * @return YResult类型的结果对象，包含审批结果或错误信息
     * <p>
     * 该方法首先调用purchaseContractAuditHandle的approval方法来处理审批逻辑
     * 如果审批成功，返回一个表示成功的YResult对象，其中包含审批的ID
     * 如果审批过程中发生异常，捕获该异常，并返回一个表示错误的YResult对象
     * 异常信息将被用于错误处理或日志记录
     */
    @Override
    public YResult<String> approval(PurchaseContractApprovalDto approvalDto) {
        // 尝试调用审批处理方法执行采购合同审批操作
        try {
            purchaseContractAuditHandle.approval(approvalDto);
            // 审批成功，返回成功结果对象，携带审批ID
            return YResult.success(approvalDto.getId());
        } catch (Exception e) {
            // 审批过程中发生异常，返回错误结果，携带异常信息
            return YResult.error(ResultMsgEnum.APPROVAL_ERROR);
        }
    }

    /**
     * 将数据访问对象（DAO）层的购买信息转换为视图对象（VO）
     *
     * @param bamuPurchaseContract 数据访问对象层的购买合同信息
     * @return 返回转换后的视图对象（VO）层的购买信息
     */
    private PurchaseInfoVO daoToInfoVO(BamuPurchaseContract bamuPurchaseContract) {
        // 创建视图对象实例
        PurchaseInfoVO purchaseInfoVO = new PurchaseInfoVO();
        // 使用BeanUtils工具类复制数据访问对象的属性到视图对象
        BeanUtils.copyProperties(bamuPurchaseContract, purchaseInfoVO);
        // 返回填充好的视图对象
        return purchaseInfoVO;
    }

    /**
     * 将数据访问对象（DAO）列表转换为值对象（VO）列表
     * 该方法主要用于处理采购合同数据，将它们从DAO对象转换为VO对象，以便在不同层面上使用
     *
     * @param records 采购合同的DAO对象列表
     * @return 返回转换后的VO对象列表
     */
    private List<PurchaseVO> daoToVoList(List<BamuPurchaseContract> records) {
        // 从记录中提取并去重合同ID
        List<String> contractIdList = records.stream().map(BamuPurchaseContract::getId).distinct().toList();
        // 创建一个映射，用于存储合同ID与合同汇款信息的对应关系
        Map<String, BamuContractWirePay> contractWirePayMap = new HashMap<>();
        // 如果合同ID列表不为空，则通过服务查询并填充合同汇款信息到映射中
        if (CollectionUtils.isNotEmpty(contractIdList)) {
            contractWirePayMap.putAll(iBamuContractWirePayService.mapByContractIdList(contractIdList));
        }
        // 将DAO对象列表转换为VO对象列表，并返回
        return records.stream().map(x -> daoToVo(x, contractWirePayMap)).collect(Collectors.toList());
    }

    /**
     * 将数据访问对象（DAO）层的数据转换为视图对象（VO）层的数据
     * 此方法主要用于处理采购合同数据，将其从DAO层的格式转换为VO层的格式
     * 如果存在与采购合同关联的电线支付信息，则将电线支付的类型也一并转换
     *
     * @param bamuPurchaseContract 采购合同的DAO层对象，包含详细的采购合同信息
     * @param contractWirePayMap   电线支付信息的映射，使用合同ID作为键，以便快速查找与合同关联的电线支付信息
     * @return 返回转换后的采购合同视图对象（PurchaseVO），用于展示或进一步处理
     */
    private PurchaseVO daoToVo(BamuPurchaseContract bamuPurchaseContract, Map<String, BamuContractWirePay> contractWirePayMap) {
        // 创建一个空的采购合同视图对象，用于填充从DAO层对象转换来的数据
        PurchaseVO purchaseVO = new PurchaseVO();
        // 使用BeanUtils工具类，将采购合同的DAO层对象的数据复制到视图对象中
        BeanUtils.copyProperties(bamuPurchaseContract, purchaseVO);
        purchaseVO.setSupplyDeptName(bamuPurchaseContract.getSupplyDeptName());
        BigDecimal contractTotalRealPrice = bamuPurchaseContract.getContractTotalRealPrice();
        BigDecimal contractRebatePoints = bamuPurchaseContract.getContractRebatePoints() == null ? BigDecimal.ZERO :bamuPurchaseContract.getContractRebatePoints();
        purchaseVO.setContractRebatePrice(contractRebatePoints.compareTo(BigDecimal.ZERO) > 0 ? contractRebatePoints.divide(BigDecimal.valueOf(100)
                , 2, RoundingMode.HALF_UP).multiply(contractTotalRealPrice) : BigDecimal.ZERO);

        //获取合同id
        String contractId = bamuPurchaseContract.getId();
        //根据合同id，获取历史结算单
        List<BamuSpReconciliationSettlement> list = iBamuSpReconciliationSettlementService.lambdaQuery()
                .eq(BamuSpReconciliationSettlement::getContractId, contractId)
                .eq(BamuSpReconciliationSettlement::getIsDeleted, DataDeletedStatus.NO.getType()).list();
        if(CollectionUtils.isNotEmpty(list)){
            List<String> settlementIdList = list.stream().map(BamuSpReconciliationSettlement::getId).distinct().toList();
            //查询材料表，获取所有结算单的历史结算金额
            List<BamuSpReconciliationSettlementOrderMaterial> resultList = iBamuSpReconciliationSettlementOrderMaterialService.lambdaQuery()
                    .in(BamuSpReconciliationSettlementOrderMaterial::getReconciliationSettlementId, settlementIdList)
                    .list();
            if(CollectionUtils.isNotEmpty(resultList)){
                BigDecimal sum = resultList.stream().map(BamuSpReconciliationSettlementOrderMaterial::getReconciliationPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
                purchaseVO.setHistorySettlementAmount(sum);
            }
        }

        // 从电线支付信息映射中，通过合同ID尝试获取与当前采购合同关联的电线支付信息
        BamuContractWirePay contractWirePay = contractWirePayMap.get(bamuPurchaseContract.getId());
        // 如果找到了关联的电线支付信息，则将其类型设置到采购合同的视图对象中
        if (Objects.nonNull(contractWirePay)) {
            purchaseVO.setPriceType(contractWirePay.getPriceType());
            purchaseVO.setPaymentCycle(contractWirePay.getPaymentCycle());
            purchaseVO.setPriceTypeDesc(ContractPriceType.typeOfDesc(Integer.valueOf(contractWirePay.getPriceType())));
        }
        // 获取审核状态
        String auditStatus = bamuPurchaseContract.getAuditStatus();
        // 获取流程实例ID
        String processInstanceId = bamuPurchaseContract.getProcessInstanceId();
        // 获取模型方案信息ID
        String modelSchemeInfoId = bamuPurchaseContract.getModelSchemeInfoId();
        // 根据审核状态和其他条件设置审核按钮状态
        if (StringUtils.isNotBlank(auditStatus)
                && StringUtils.equals(auditStatus, AuditStatusEnums.AUDIT.getType().toString())
                && StringUtils.isNotBlank(modelSchemeInfoId)
                && StringUtils.isNotBlank(processInstanceId)) {
            purchaseVO.setAuditButton(purchaseContractAuditHandle.userIsAuditButton(modelSchemeInfoId,
                    processInstanceId));
        }
        // 返回填充完毕的采购合同视图对象
        return purchaseVO;
    }


    /**
     * 批量导入物料信息
     * <p>
     * 此方法主要用于从Excel文件中导入物料信息到系统中它首先解析上传的文件，
     * 获取物料数据，然后将这些数据转换为系统可以存储的对象最后，批量保存这些
     * 物料信息到数据库中
     *
     * @param importDto 包含合同ID和物料文件的导入数据传输对象
     * @return YResult对象，包含导入结果信息如果导入成功，返回成功的YResult对象和合同ID；
     * 如果导入失败，返回失败的YResult对象和错误信息
     */
    @Override
    public YResult<String> importContractMaterial(ContractMaterialImportDto importDto) {
        try {
            // 获取合同ID，用于后续将导入的物料信息关联到对应的合同上
            String contractId = importDto.getContractId();
            // 从导入数据传输对象中获取文件
            MultipartFile file = importDto.getFile();
            if (file == null) {
                return YResult.error(ResultMsgEnum.IMPORT_FILE_NOT_FOUND);
            }

            // 验证文件大小
            long maxSize = 1024 * 1024 * 50;
            // 表示文件的最大允许大小为50MB (1024 * 1024 * 50 字节)
            if (file.getSize() > maxSize) {
                return YResult.error(ResultMsgEnum.IMPORT_FILE_SIZE_ERROR);
            }

            // 解析文件，将Excel中的物料信息转换为系统能够处理的DTO列表
            List<ContractMaterialExcelDto> importDtoList =
                    purchaseContractMaterialExcelImportHandle.importContractMaterialExcel(file);

            // 检查解析结果，如果没有数据，则返回错误提示
            if (CollectionUtils.isEmpty(importDtoList)) {
                return YResult.error(ResultMsgEnum.IMPORT_NO_DATA);
            }

            // 将解析后的DTO列表和合同ID转换为数据库访问对象（DAO）列表
            List<BamuContractMaterial> oldDaoList = iBamuContractMaterialService.listByContractId(contractId);
            if (CollectionUtils.isEmpty(oldDaoList)){
                return YResult.error(ResultMsgEnum.IMPORT_NO_ORGIN_DATA);
            }
            if (oldDaoList.size()!=importDtoList.size()) {
                return YResult.error("导入列表数量与原合同材料不一致，请核实");
            }
            List<BamuContractMaterial> daoList = importPurchaseDtoToDaoList(importDtoList, contractId);

            // 批量保存物料信息到数据库
            iBamuContractMaterialService.saveOrUpdateBatch(daoList, 100);

            // 返回导入成功的提示信息和合同ID
            return YResult.success(contractId);
        } catch (BusinessException e) {
            log.warn("导入业务异常 ", e);
            return YResult.error(e.getMessage());
        } catch (IllegalArgumentException e) {
            log.error("Invalid argument provided: ", e);
            return YResult.error(ResultMsgEnum.IMPORT_INVALID_ARGUMENT);
        } catch (Exception e) {
            log.error("Unexpected error during import process: ", e);
            return YResult.error(ResultMsgEnum.IMPORT_UNEXPECTED_ERROR);
        }
    }

    @Override
    public List<ContractMaterialExcelDto> exportMaterial(String contractId) {
        List<BamuContractMaterial> contractMaterialList  = iBamuContractMaterialService.listByContractId(contractId);
        if (CollectionUtils.isNotEmpty(contractMaterialList)){
            return contractMaterialList.stream().map(this::convertToExcelDto).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }


    /**
     * 将导入的DTO对象列表转换为持久层对象列表
     * 本方法解释了为什么需要进行转换：为了将外部数据导入到数据库中，我们需要先将这些数据转换为数据库层可以理解的格式
     *
     * @param importDtoList 从Excel导入的合同材料DTO列表
     * @param contractId    合同ID，用于关联导入材料的合同信息
     * @return 返回转换后的持久层对象列表
     */
    private List<BamuContractMaterial> importPurchaseDtoToDaoList(List<ContractMaterialExcelDto> importDtoList, String contractId) {
        return importDtoList.stream()
                .map(x -> importPurchaseDtoToDao(x, contractId))
                .collect(Collectors.toList());
    }


    /**
     * 将Excel导入的材料信息DTO转换为数据库实体
     * <p>
     * 此方法负责将用户从Excel文件中导入的材料数据转换为数据库中使用的实体对象
     * 它提取材料的相关信息，并将其映射到数据库实体的属性上
     *
     * @param excelImportDto 从Excel导入的材料信息数据传输对象
     * @param contractId     合同ID，用于将材料信息关联到特定的合同
     * @return 返回填充了材料信息的数据库实体对象
     */
    private BamuContractMaterial importPurchaseDtoToDao(ContractMaterialExcelDto excelImportDto,
                                                        String contractId) {
        // 获取当前用户信息，用于后续设置租户ID等信息
        UserInfoRequest userInfo = BaseContext.getUserInfo();

        // 覆盖存在的材料
        BamuContractMaterial bamuContractMaterial = iBamuContractMaterialService.getById(excelImportDto.getId());
        if (bamuContractMaterial == null) {
            bamuContractMaterial = new BamuContractMaterial();
        }

        // 设置合同ID，将材料信息与特定合同关联起来
        bamuContractMaterial.setContractId(contractId);

        // 以下代码将DTO中的材料信息映射到数据库实体的相应属性上
        bamuContractMaterial.setMaterialId(excelImportDto.getMaterialId());
        bamuContractMaterial.setMaterialCode(excelImportDto.getMaterialCode());
        bamuContractMaterial.setMaterialName(excelImportDto.getMaterialName());
        bamuContractMaterial.setMaterialSpecs(excelImportDto.getMaterialSpecs());
        bamuContractMaterial.setMaterialModel(excelImportDto.getMaterialModel());
        bamuContractMaterial.setMaterialUnit(excelImportDto.getMaterialUnit());
        bamuContractMaterial.setMaterialBrand(excelImportDto.getMaterialBrand());
        bamuContractMaterial.setMaterialNum(excelImportDto.getMaterialNum());
        bamuContractMaterial.setMaterialTaxCode(excelImportDto.getMaterialTaxCode());
        bamuContractMaterial.setMaterialTaxShortName(excelImportDto.getMaterialTaxShortName());
        bamuContractMaterial.setExcludingTaxUnitPrice(excelImportDto.getExcludingTaxUnitPrice());
        bamuContractMaterial.setTax(excelImportDto.getTax());
        bamuContractMaterial.setIncludingTaxUnitPrice(excelImportDto.getIncludingTaxUnitPrice());
        bamuContractMaterial.setIncludingTaxTotalPrice(excelImportDto.getIncludingTaxTotalPrice());
        bamuContractMaterial.setMaterialMark(excelImportDto.getMaterialMark());
        bamuContractMaterial.setStatus(1);

        // 设置租户ID，用于支持多租户环境下数据的隔离
        bamuContractMaterial.setTenantId(userInfo.getTenantId());

        // 以下属性设置是否新增、材料排序等信息，用于数据库中的逻辑处理
        bamuContractMaterial.setMaterialSortNum(excelImportDto.getMaterialSortNum());

        // 返回填充完毕的数据库实体对象
        return bamuContractMaterial;
    }


    /**
     * 将BamuContractMaterial转化成ContractMaterialExportDto
     */
    private ContractMaterialExcelDto convertToExcelDto(BamuContractMaterial material){
        ContractMaterialExcelDto dto = new ContractMaterialExcelDto();
        BeanUtils.copyProperties(material,dto);
        dto.setId(material.getId());
        return dto;
    }

    @Override
    public List<ContractExcelDto> export(PurchaseDto purchaseDto) {
        // 构建查询条件
        LambdaQueryWrapper<BamuPurchaseContract> queryWrapper = new LambdaQueryWrapper<BamuPurchaseContract>()
                .eq(BamuPurchaseContract::getContractType, purchaseDto.getContractType())
                .eq(BamuPurchaseContract::getIsDeleted, DataDeletedStatus.NO.getType())
                .in(CollectionUtils.isNotEmpty(BaseContext.getProjectList()), BamuPurchaseContract::getProjectId, BaseContext.getProjectList())
                .in(CollectionUtils.isNotEmpty(BaseContext.getSupplierList()), BamuPurchaseContract::getSupplierDeptId, BaseContext.getSupplierList())
                .between(StringUtils.isNotBlank(purchaseDto.getBeginDate()) && StringUtils.isNotBlank(purchaseDto.getEndDate()), BamuPurchaseContract::getSignDate,
                        ConvertUtil.getStartOfDay(ConvertUtil.getString2Date(purchaseDto.getBeginDate())), ConvertUtil.getEndOfDay(ConvertUtil.getString2Date(purchaseDto.getEndDate())))
                .like(StringUtils.isNotBlank(purchaseDto.getContractNo()), BamuPurchaseContract::getContractNo, purchaseDto.getContractNo())
                .like(StringUtils.isNotBlank(purchaseDto.getContractName()), BamuPurchaseContract::getContractName, purchaseDto.getContractName())
                .eq(StringUtils.isNotBlank(purchaseDto.getProjectId()), BamuPurchaseContract::getProjectId, purchaseDto.getProjectId())
                .like(StringUtils.isNotBlank(purchaseDto.getProjectName()), BamuPurchaseContract::getProjectName, purchaseDto.getProjectName())
                .eq(StringUtils.isNotBlank(purchaseDto.getSupplierDeptId()), BamuPurchaseContract::getSupplierDeptId, purchaseDto.getSupplierDeptId())
                .like(StringUtils.isNotBlank(purchaseDto.getSupplierDeptName()), BamuPurchaseContract::getSupplierDeptName, purchaseDto.getSupplierDeptName())
                .like(StringUtils.isNotBlank(purchaseDto.getMainContractNo()), BamuPurchaseContract::getMainContractNo, purchaseDto.getMainContractNo())
                .like(StringUtils.isNotBlank(purchaseDto.getMainContractName()), BamuPurchaseContract::getMainContractName, purchaseDto.getMainContractName())
                .like(StringUtils.isNotBlank(purchaseDto.getProjectDeptName()),
                        BamuPurchaseContract::getProjectDeptName, purchaseDto.getProjectDeptName())
                .eq(StringUtils.isNotBlank(purchaseDto.getContractMethod()), BamuPurchaseContract::getContractMethod
                        , purchaseDto.getContractMethod())
                .orderByDesc(BamuPurchaseContract::getCreatedAt)
                .orderByDesc(BamuPurchaseContract::getId);
        List<BamuPurchaseContract> contractMaterialList  = iBamuPurchaseContractService.list(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractMaterialList)){
            return contractMaterialList.stream().map(this::convertToExcelDto2).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }
    /**
     * 将BamuContractMaterial转化成ContractMaterialExportDto
     */
    private ContractExcelDto convertToExcelDto2(BamuPurchaseContract bamuPurchaseContract){
        ContractExcelDto dto = new ContractExcelDto();
        BeanUtils.copyProperties(bamuPurchaseContract,dto);
        dto.setId(bamuPurchaseContract.getId());
        return dto;
    }

}
